//
//  BCRouter.m
//  BCRouter_Example
//
//  Created by 蔡亮 on 2019/3/25.
//  Copyright © 2019年 bruce. All rights reserved.
//

#import "BCRouter.h"

static NSString *const FFRouterWildcard = @"*";
static NSString *FFSpecialCharacters = @"/?&.";

static NSString *const FFRouterCoreKey = @"FFRouterCore";
static NSString *const FFRouterCoreBlockKey = @"FFRouterCoreBlock";
static NSString *const FFRouterCoreTypeKey = @"FFRouterCoreType";

NSString *const FFRouterParameterURLKey = @"FFRouterParameterURL";

typedef NS_ENUM(NSInteger, BCRouterType) {
    BCRouterTypeDefault = 0,
    BCRouterTypeObject = 1,
    BCRouterTypeCallback = 2,
};

@interface BCRouter ()
@property (nonatomic, strong) NSMutableDictionary *routerDic;
@property (nonatomic, copy) BCRouterUnregisterURLHandler routerUnregisterURLHandler;
@property (nonatomic, strong) dispatch_queue_t queue;

@end

@implementation BCRouter
+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    static BCRouter *router = nil;
    dispatch_once(&onceToken, ^{
        router = [[BCRouter alloc]init];
    });
    return router;
}
- (instancetype)init
{
    if (self = [super init]) {
        _queue = dispatch_queue_create("串行队列防止任务竞争", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}
#pragma mark - public methods
+ (void)registerRouterURL:(NSString *)url handler:(BCRouterHandler)handler
{
    [[self sharedInstance] addRouterURL:url handler:handler];
}
+ (void)registerObjectRouterURL:(NSString *)url handler:(BCObjectRouterHandler)handler
{
    [[self sharedInstance] addObjectRouterURL:url handler:handler];
}
+ (void)registerCallbackRouterURL:(NSString *)url handler:(BCCallbackRouterHandler)handler
{
    [[self sharedInstance] addCallbackRouterURL:url handler:handler];
}
#pragma mark - 是否能够链接到此URL
+ (BOOL)canRouterURL:(NSString *)url
{
    return [[self sharedInstance] achieveParametersFromURL:url] ? true : false;
}
+ (void)routerToURL:(nullable NSString *)url
{
    [self routerToURL:url params:nil];
}
#pragma mark - 指向到URL 先找到注册的URL的参数（block）
+ (void)routerToURL:(NSString *)url params:(nullable NSDictionary *)parameters
{
    url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSMutableDictionary * routerParameters = [[self sharedInstance] achieveParametersFromURL:url];
    if (!routerParameters) {
        [[self sharedInstance]unregisterURLBeRouterWithURL:url];
        return;
    }
    [routerParameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
        if ([obj isKindOfClass:[NSString class]]) {
            routerParameters[key] = [NSString stringWithFormat:@"%@",obj];
        }
    }];
    if (routerParameters) {
        NSDictionary *coreDic = routerParameters[FFRouterCoreKey];
        BCRouterHandler handler = coreDic[FFRouterCoreBlockKey];
        BCRouterType type = [coreDic[FFRouterCoreTypeKey] integerValue];
        if (type != BCRouterTypeDefault) {
            return;
        }
        
        if (handler) {
            if (parameters) {
                [routerParameters addEntriesFromDictionary:parameters];
            }
            [routerParameters removeObjectForKey:FFRouterCoreKey];
            handler(routerParameters);
        }
    }
}

+ (id)routerObjectURL:(NSString *)url
{
    return [self routerObjectURL:url params:nil];
}

+ (id)routerObjectURL:(NSString *)url params:(nullable NSDictionary *)parameters
{
    url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSMutableDictionary * routerParameters = [[self sharedInstance] achieveParametersFromURL:url];
    if (!routerParameters) {
        [[self sharedInstance]unregisterURLBeRouterWithURL:url];
        return nil;
    }
    [routerParameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
        if ([obj isKindOfClass:[NSString class]]) {
            routerParameters[key] = [NSString stringWithFormat:@"%@",obj];
        }
    }];
    if (routerParameters) {
        NSDictionary *coreDic = routerParameters[FFRouterCoreKey];
        BCObjectRouterHandler handler = coreDic[FFRouterCoreBlockKey];
        BCRouterType type = [coreDic[FFRouterCoreTypeKey] integerValue];
        if (type != BCRouterTypeObject) {
            return nil;
        }
        if (handler) {
            if (parameters) {
                [routerParameters addEntriesFromDictionary:parameters];
            }
            [routerParameters removeObjectForKey:FFRouterCoreKey];
            return  handler(routerParameters);
        }
    }
    return nil;
}

+ (void)routerCallbackURL:(NSString *)url targetCallback:(BCRouterCallback)targetCallback
{
    [self routerCallbackURL:url params:nil targetCallback:targetCallback];
}
+ (void)routerCallbackURL:(NSString *)url params:(nullable NSDictionary<NSString *,id> *)parameters targetCallback:(BCRouterCallback)targetCallback
{
    url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSMutableDictionary * routerParameters = [[self sharedInstance] achieveParametersFromURL:url];
    if (!routerParameters) {
        [[self sharedInstance]unregisterURLBeRouterWithURL:url];
        return;
    }
    [routerParameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
        if ([obj isKindOfClass:[NSString class]]) {
            routerParameters[key] = [NSString stringWithFormat:@"%@",obj];
        }
    }];
    if (routerParameters) {
        NSDictionary *coreDic = routerParameters[FFRouterCoreKey];
        BCCallbackRouterHandler handler = coreDic[FFRouterCoreBlockKey];
        BCRouterType type = [coreDic[FFRouterCoreTypeKey] integerValue];
        if (type != BCRouterTypeObject) {
            return;
        }
        if (parameters) {
            [routerParameters addEntriesFromDictionary:parameters];
        }
        if (handler) {
            [routerParameters removeObjectForKey:FFRouterCoreKey];
            handler(routerParameters,^(id callbackObjc){
                if (targetCallback) {
                    targetCallback(callbackObjc);
                }
            });
        }
    }
}
+ (void)removeRouterWithURL:(NSString *)url
{
    [[self sharedInstance]doRemoveRouterURL:url];
}
+ (void)removeAllRouter
{
    [[self sharedInstance] doRemoveAllRouterURL];
}


#pragma mark - pravite method
- (void)addRouterURL:(NSString *)routerUrl handler:(BCRouterHandler)handlerBlock
{
    NSMutableDictionary *subRouters = [self addURLPattern:routerUrl];
    if (handlerBlock && subRouters) {
        NSDictionary *coreDic = @{FFRouterCoreBlockKey:[handlerBlock copy],FFRouterCoreTypeKey: @(BCRouterTypeDefault)};
        subRouters[FFRouterCoreKey] = coreDic;
    }
}
- (void)addObjectRouterURL: (NSString *)routerUrl handler:(BCObjectRouterHandler)handlerBlock{
    NSMutableDictionary *subRouters = [self addURLPattern:routerUrl];
    if (handlerBlock && subRouters) {
        NSDictionary *coreDic = @{FFRouterCoreBlockKey:[handlerBlock copy],FFRouterCoreTypeKey: @(BCRouterTypeObject)};
        subRouters[FFRouterCoreKey] = coreDic;
    }
}
- (void)addCallbackRouterURL:(NSString *)routerUrl handler:(BCCallbackRouterHandler)handlerBlock{
    NSMutableDictionary *subRouters = [self addURLPattern:routerUrl];
    if (handlerBlock && subRouters) {
        NSDictionary *coreDic = @{FFRouterCoreBlockKey:[handlerBlock copy],FFRouterCoreTypeKey: @(BCRouterTypeCallback)};
        subRouters[FFRouterCoreKey] = coreDic;
    }
}


- (NSMutableDictionary *)addURLPattern:(NSString *)URLPattern{
    NSArray *pathComponents = [self pathComponentsFromURL:URLPattern];
    __block NSMutableDictionary *subRouters = self.routerDic;
    dispatch_async(_queue, ^{
        for (NSString *pathComponent in pathComponents) {
            if (![subRouters objectForKey:pathComponent]) {
                subRouters[pathComponent] = [NSMutableDictionary dictionary];
            }
            subRouters = subRouters[pathComponent];
        }
    });
    
    return subRouters;
}

- (void)unregisterURLBeRouterWithURL:(NSString *)URL {
    if (self.routerUnregisterURLHandler) {
        self.routerUnregisterURLHandler(URL);
    }
}

- (void)doRemoveRouterURL:(NSString *)routerUrl{
    if (self.routerDic.count <= 0) {
        return;
    }
    NSMutableArray *pathComponents = [NSMutableArray arrayWithArray:[self pathComponentsFromURL:routerUrl]];
    BOOL firstPoll = YES;
    
    while (pathComponents.count > 0) {
        NSString *componentKey = [pathComponents componentsJoinedByString:@"."];
        NSMutableDictionary *router = [self.routerDic valueForKeyPath:componentKey];
        if (router.count > 1 && firstPoll) {
            [router removeObjectForKey:FFRouterCoreKey];
            break;
        }
        if (router.count <=1 && firstPoll) {
            NSString *lastComponent = [pathComponents lastObject];
            [pathComponents removeLastObject];
            NSString *parentComponent = [pathComponents componentsJoinedByString:@"."];
            router = [self.routerDic valueForKey:parentComponent];
            [router removeObjectForKey:lastComponent];
            firstPoll = false;
            continue;
        }
        if (router.count>0 && !firstPoll) {
            break;
        }
    }
}
- (void)doRemoveAllRouterURL{
    dispatch_async(_queue, ^{
        [self.routerDic removeAllObjects];
    });
}

- (NSArray*)pathComponentsFromURL:(NSString*)URL {
    
    NSMutableArray *pathComponents = [NSMutableArray array];
    if ([URL rangeOfString:@"://"].location != NSNotFound) {
        NSArray *pathSegments = [URL componentsSeparatedByString:@"://"];
        [pathComponents addObject:pathSegments[0]];
        for (NSInteger idx = 1; idx < pathSegments.count; idx ++) {
            if (idx == 1) {
                URL = [pathSegments objectAtIndex:idx];
            }else{
                URL = [NSString stringWithFormat:@"%@://%@",URL,[pathSegments objectAtIndex:idx]];
            }
        }
    }
    
    if ([URL hasPrefix:@":"]) {
        if ([URL rangeOfString:@"/"].location != NSNotFound) {
            NSArray *pathSegments = [URL componentsSeparatedByString:@"/"];
            [pathComponents addObject:pathSegments[0]];
        }else{
            [pathComponents addObject:URL];
        }
    }else{
        for (NSString *pathComponent in [[NSURL URLWithString:URL] pathComponents]) {
            if ([pathComponent isEqualToString:@"/"]) continue;
            if ([[pathComponent substringToIndex:1] isEqualToString:@"?"]) break;
            [pathComponents addObject:pathComponent];
        }
    }
    return [pathComponents copy];
}
#pragma mark - 解析完URL判断是否在列表中
- (NSMutableDictionary *)achieveParametersFromURL:(NSString *)url{
    NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
    parameters[FFRouterParameterURLKey] = [url stringByRemovingPercentEncoding];
    
    NSMutableDictionary *subRoutes = self.routerDic;
    NSArray *pathComponents = [self pathComponentsFromURL:url];
    
    NSInteger pathComponentsSurplus = [pathComponents count];
    BOOL wildcardMatched = false;
    
    for (NSString* pathComponent in pathComponents) {
        NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch;
        NSArray *subRoutesKeys =[subRoutes.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
            return [obj2 compare:obj1 options:comparisonOptions];
        }];
        
        for (NSString* key in subRoutesKeys) {
            
            if([pathComponent isEqualToString:key]){
                pathComponentsSurplus --;
                subRoutes = subRoutes[key];
                break;
            }else if([key hasPrefix:@":"] && pathComponentsSurplus == 1){
                subRoutes = subRoutes[key];
                NSString *newKey = [key substringFromIndex:1];
                NSString *newPathComponent = pathComponent;
                
                NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:FFSpecialCharacters];
                NSRange range = [key rangeOfCharacterFromSet:specialCharacterSet];
                
                if (range.location != NSNotFound) {
                    newKey = [newKey substringToIndex:range.location - 1];
                    NSString *suffixToStrip = [key substringFromIndex:range.location];
                    newPathComponent = [newPathComponent stringByReplacingOccurrencesOfString:suffixToStrip withString:@""];
                }
                parameters[newKey] = newPathComponent;
                break;
            }else if([key isEqualToString:FFRouterWildcard] && !wildcardMatched){
                subRoutes = subRoutes[key];
                wildcardMatched = YES;
                break;
            }
        }
    }
    
    if (!subRoutes[FFRouterCoreKey]) {
        return nil;
    }
    
    NSArray<NSURLQueryItem *> *queryItems = [[NSURLComponents alloc] initWithURL:[[NSURL alloc] initWithString:url] resolvingAgainstBaseURL:false].queryItems;
    
    for (NSURLQueryItem *item in queryItems) {
        parameters[item.name] = item.value;
    }
    
    parameters[FFRouterCoreKey] = [subRoutes[FFRouterCoreKey] copy];
    return parameters;

}

- (NSMutableDictionary *)routerDic
{
    if (!_routerDic) {
        _routerDic = [[NSMutableDictionary alloc]init];
    }
    return _routerDic;
}
@end
